#include "networking.h"
#include <stdarg.h>

using namespace std;

ssize_t
socketstream::read_char(char *ptr)
{
  if (read_cnt_ <= 0) {
  again:
    if ( (read_cnt_ = read(sockfd_, read_buf_, sizeof(read_buf_))) < 0) {
      if (errno == EINTR)
	goto again;
      return(-1);
    } else if (read_cnt_ == 0)
      return(0);
    read_ptr_ = read_buf_;
  }
  
  read_cnt_--;
  *ptr = *read_ptr_++;
  return(1);
}

// just a wrapper of private_read_line(), for ease of debuging
ssize_t
socketstream::read_line(char *line, size_t maxlen)
{
  int rn = private_read_line(line, maxlen);
  if (debug) cerr << "[Line: " << rn << " ]" << line << endl;
  return rn;
}

/* 
 * read one line, note that \r\n or \n are not returned
 * return value: -1 if error occurs; 0 if empty line; line length otherwise
 */
ssize_t
socketstream::private_read_line(char *line, size_t maxlen)
{
  size_t rc, n;
  char	 *ptr;

  ptr = line;
  for (n = 1; n < maxlen; n++) {
    rc = read_char(ptr);
    if (rc < 0) { // error
      return -1;
    } else if (rc == 0) { // EOF
      *ptr = '\0';
      return --n;
    } else if (*ptr == '\n') { // read \n
      if (n > 1 && *(ptr-1) == '\r') { // check if \r before \n
	ptr--;
	n--;
      }
      n--;
      *ptr = '\0';
      return n;
    } 
    ptr++;
  }

  // buffer is full at this point
  *(++ptr) = '\0';
  return --n;
}


/* 
 * keep on reading until read n or read 0 
 */ 
ssize_t
socketstream::readn_socket(char *dstptr, size_t n)
{
  size_t	nleft;
  ssize_t	nread;
  char	       *ptr;
  
  ptr = dstptr;
  nleft = n;
  while (nleft > 0) {
    if ( (nread = read(sockfd_, ptr, nleft)) < 0) {
      if (errno == EINTR)
	nread = 0;		/* and call read() again */
      else
	return(-1);
    } else if (nread == 0)
      break;				/* EOF */
    
    nleft -= nread;
    ptr   += nread;
  }
  return(n - nleft);		/* return >= 0 */
}

ssize_t
socketstream::writen_buf(char *srcbuf, size_t len)
{
  char *sptr = srcbuf;
  char *dptr = &write_buf_[write_cnt_];
  size_t  n = min(len, (size_t)MAXBUF-write_cnt_);
  strncpy(dptr, sptr, n);

  return n;
}

ssize_t
socketstream::snprintf_buf(const char *fmt, ...)
{
  va_list   ap;
  va_start(ap, fmt);
  int n = vsnprintf(&write_buf_[write_cnt_], MAXBUF-write_cnt_, fmt, ap);
  va_end(ap);
  write_cnt_ += n;
  return n;
}

ssize_t
socketstream::flush_buf()
{
  int n = writen_socket(write_buf_, write_cnt_);
  write_cnt_ = 0;
  return n;
}

ssize_t
socketstream::writen_socket(char *srcbuf, size_t n)
{
  size_t		nleft;
  ssize_t		nwritten;
  char	               *ptr;
  
  ptr = srcbuf;
  nleft = n;
  while (nleft > 0) {
    if ( (nwritten = write(sockfd_, ptr, nleft)) <= 0) {
      if (errno == EINTR)
	nwritten = 0;		/* and call write() again */
      else
	return(-1);		/* error */
    }
    
    nleft -= nwritten;
    ptr   += nwritten;
  }
  return(n);
}
